#pragma once
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <mutex>
#include <queue>
#include"LockGuard.hpp"
using namespace std;
#define DefaultValue 5
template <class T>
class BlockQueue
{
public:
    BlockQueue(int defaultnumber = DefaultValue)
        : _defaultnumber(defaultnumber)
    {

        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_Producer_Cond, nullptr);
        pthread_cond_init(&_Consumer_Cond, nullptr);
    }
    void Push(T &in)
    {
        //pthread_mutex_lock(&_mtx);
        LockGuard lockguard(&_mtx);

        //因为只要是函数，就可能存在调用失败的情况，并且还可能存在线程被伪唤醒的情况
        //所以当当前线程被唤醒的时候，应该再检测一次条件是否满足，所以应该使用while进行判断，不能用if
        while(IsFull())//当前队列为满，生产者线程阻塞
        {
            pthread_cond_wait(&_Producer_Cond,&_mtx);//成功调用wait之后，会把当前持有的锁释放掉，当被成功唤醒后，wait会自动申请获得锁
        }
        //可以往缓冲区内生产任务了
        _blockqueue.push(in);
        pthread_cond_signal(&_Consumer_Cond);//唤醒消费者线程
        // pthread_mutex_unlock(&_mtx);
    }
    void Pop(T* out)
    {
         LockGuard lockguard(&_mtx);
         //pthread_mutex_lock(&_mtx);
        while(IsEmpty())//当前队列为空，消费者线程阻塞
        {
            pthread_cond_wait(&_Consumer_Cond,&_mtx);
        }
        //可以从缓冲区内获得任务了
       *out=_blockqueue.front();
       _blockqueue.pop();
        pthread_cond_signal(&_Producer_Cond);//取了任务以后唤醒生产者进程
        // pthread_mutex_unlock(&_mtx);
        //自动调用lockguard的析构函数解锁
    }

    ~BlockQueue()
    {
        
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_Producer_Cond);
        pthread_cond_destroy(&_Consumer_Cond);
    }

private:
    queue<T> _blockqueue;//阻塞队列
    int _defaultnumber;//容量上限
    pthread_mutex_t _mtx;//互斥锁
    pthread_cond_t _Producer_Cond;//表示阻塞队列是否满的条件
    pthread_cond_t _Consumer_Cond;//表示阻塞队列是否为空的条件
    bool IsFull()
    {
        return _blockqueue.size() == _defaultnumber;
    }
    bool IsEmpty()
    {
        return _blockqueue.empty();
    }
};